package com.frcdi.qms.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.frcdi.qms.common.WorkFlowConstants;
import com.frcdi.qms.common.core.domain.R;
import com.frcdi.qms.common.exception.ServiceException;
import com.frcdi.qms.common.utils.SecurityUtils;
import com.frcdi.qms.domain.ProcessTemplates;
import com.frcdi.qms.domain.TemplateHistory;
import com.frcdi.qms.dto.FlowEngineDTO;
import com.frcdi.qms.dto.json.ChildNode;
import com.frcdi.qms.dto.json.SettingsInfo;
import com.frcdi.qms.mapper.ProcessTemplatesMapper;
import com.frcdi.qms.service.ProcessTemplateService;
import com.frcdi.qms.service.SettingService;
import com.frcdi.qms.service.TemplateHistoryService;
import com.frcdi.qms.utils.IdWorker;
import com.frcdi.qms.utils.SpringContextHolder;
import com.frcdi.qms.vo.TemplateGroupVo;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.repository.Deployment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

import static com.frcdi.qms.utils.BpmnModelUtils.*;

/**
 * @author : willian fu
 * @date : 2020/9/21
 */
@Service
public class SettingServiceImpl implements SettingService {

    @Autowired
    private ProcessTemplateService processTemplateService;
    @Resource
    private IdWorker idWorker;
    @Resource
    private RepositoryService repositoryService;
    @Autowired
    private ProcessTemplatesMapper templatesMapper;
    @Autowired
    private TemplateHistoryService historyService;

    /**
     * 查询表单组
     *
     * @return 表单组数据
     */
    @Override
    public Object getFormGroups() {
      ProcessTemplatesMapper processTemplatesMapper = SpringContextHolder.getBean(ProcessTemplatesMapper.class);
        List<ProcessTemplates> allProcessTemplates = processTemplatesMapper.getAll();
        // 将所有数据根据类型分类
        Map<String, List<ProcessTemplates>> coverMap = allProcessTemplates.stream()
                .collect(Collectors.groupingBy(ProcessTemplates::getType,
                        () -> new LinkedHashMap<>(), Collectors.toList()));

        List<TemplateGroupVo> results = new ArrayList<>();
        coverMap.forEach((key, val) -> {
            List<TemplateGroupVo.Template> templates = new ArrayList<>();
            val.forEach(v -> {
                if (ObjectUtil.isNotNull(v.getTemplateId())) {
                    templates.add(TemplateGroupVo.Template.builder()
                            .formId(v.getTemplateId())
                            .remark(v.getRemark())
                            .formName(v.getTemplateName())
                            .icon(v.getIcon())
                            .isStop(v.getIsStop())
                            .updated(DateFormatUtils.format(v.getUpdated(),"yyyy年MM月dd日 HH时:mm分:ss秒"))
                            .background(v.getBackground())
                            .templateId(v.getTemplateId())
                            .logo(JSONObject.parseObject(v.getBackground(),new TypeReference<JSONObject>(){}))
                            .processControl(v.getProcessControl())
                            .version(v.getVersion())
                            .type(v.getType())
                            .build());
                }
            });
            results.add(TemplateGroupVo.builder().id(key).items(templates).build());
        });
        return R.ok(results);
    }

    /**
     * 查询表单模板数据
     *
     * @param templateId 模板id
     * @return 模板详情数据
     */
    @Override
    public Object getFormTemplateById(String templateId) {
        ProcessTemplates processTemplates = processTemplateService.getById(templateId);
        processTemplates.setLogo(processTemplates.getIcon());
        processTemplates.setFormId(processTemplates.getTemplateId());
        processTemplates.setFormName(processTemplates.getTemplateName());
        return R.ok(processTemplates);
    }
    /**
     * 编辑表单
     *
     * @param templateId 摸板ID
     * @param type       类型 stop using delete
     * @return 操作结果
     */
    @Override
    @Transactional
    public Object updateForm(String templateId, String type) {
        boolean isStop = "stop".equals(type);
      ProcessTemplates build = ProcessTemplates.builder().templateId(templateId).isStop(isStop)
          .build();
      if ("using".equals(type) || isStop) {
            processTemplateService.updateById(
                ProcessTemplates.builder().templateId(templateId).isStop(isStop).build());
        } else if ("delete".equals(type)) {
            processTemplateService.removeById(templateId);
        } else {
            return R.fail("不支持的操作");
        }
        return R.ok("操作成功");
    }

    /**
     * 编辑表单详情
     *
     * @param template 表单模板信息
     * @return 修改结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object updateFormDetail(ProcessTemplates template) {
        SettingsInfo settingsInfo = JSONObject.parseObject(template.getSettings(), new TypeReference<SettingsInfo>() {});
        ProcessTemplates processTemplates = processTemplateService.getById(template.getFormId());
        String templateId = processTemplates.getTemplateId();
        //原始数据备份
        historyService.save(TemplateHistory.builder()
                .template(templateId)
                .formItems(processTemplates.getFormItems())
                .templateName(processTemplates.getTemplateName())
                .process(processTemplates.getProcess())
                .createBy(SecurityUtils.getLoginUser().getUsername())
                .createTime(new Date())
                .version(processTemplates.getVersion())
                .flowableDefinition(processTemplates.getFlowableDefinition())
                .build());

        //新数据修改
        processTemplates.setTemplateName(template.getFormName());
        processTemplates.setFormItems(template.getFormItems());
        processTemplates.setSettings(template.getSettings());
        processTemplates.setProcess(template.getProcess());
        processTemplates.setIcon(template.getLogo());
        processTemplates.setBackground(template.getLogo());
        processTemplates.setNotify(settingsInfo.getNotify().toJSONString());
        String adminInfo = JSONObject.toJSONString(settingsInfo.getAdmin());
        processTemplates.setWhoCommit(adminInfo);

        processTemplates.setWhoEdit(adminInfo);
        processTemplates.setWhoExport(adminInfo);
        processTemplates.setRemark(template.getRemark());
        processTemplates.setUpdated(new Date());
        processTemplates.setProcessControl(template.getProcessControl());
        processTemplates.setVersion(processTemplates.getVersion()+1);
        processTemplates.setType(template.getType());
        processTemplateService.updateById(processTemplates);
        ChildNode childNode = JSONObject.parseObject(template.getProcess(), new TypeReference<ChildNode>(){});
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("processJson",template.getProcess());
        jsonObject.put("formJson",template.getFormItems());
        BpmnModel bpmnModel = assemBpmnModel(jsonObject, childNode, template.getRemark(),
            template.getFormName(), template.getType(), template.getFormId());
        Deployment deploy = repositoryService.createDeployment()
                .addBpmnModel(template.getFormName() + ".bpmn", bpmnModel)
                .name(template.getFormName())
                .category(template.getType() + "")//流程部署类别
                .deploy();
        String processDefinitionId = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult().getId();
        processTemplateService.updateById(ProcessTemplates.builder().templateId(templateId).flowableDefinition(processDefinitionId).build());
        return R.ok("发布更新后的表单成功");
    }

    @Override
    public List<ProcessTemplates> getFormList(ProcessTemplates template) {
        LambdaQueryWrapper<ProcessTemplates> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(ProcessTemplates::getType, template.getType())
                .eq(ProcessTemplates::getTemplateId,template.getTemplateId())
                .eq(ProcessTemplates::getProcessControl,template.getProcessControl());
        return templatesMapper.selectList(queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String jsonToBpmn(FlowEngineDTO flowEngineDTO) throws InvocationTargetException, IllegalAccessException {
        String processJson = flowEngineDTO.getProcess();
        ChildNode childNode = JSONObject.parseObject(processJson, new TypeReference<ChildNode>(){});
        String settingsJson = flowEngineDTO.getSettings();
        SettingsInfo settingsInfo = JSONObject.parseObject(settingsJson, new TypeReference<SettingsInfo>() {});
        String remark = flowEngineDTO.getRemark();
        String formItems = flowEngineDTO.getFormItems();
        String formName = flowEngineDTO.getFormName();
        String logo = flowEngineDTO.getLogo();
        String templateId = idWorker.nextId()+"";
        String processControl= flowEngineDTO.getProcessControl();
        Integer version= flowEngineDTO.getVersion();
        String type=flowEngineDTO.getType();

        ProcessTemplates processTemplates = ProcessTemplates.builder().build();
        processTemplates.setTemplateId(templateId);
        processTemplates.setTemplateName(formName);
        processTemplates.setFormItems(formItems);
        processTemplates.setProcess(processJson);
        processTemplates.setIcon(logo);
        processTemplates.setBackground(logo);
        processTemplates.setNotify(settingsInfo.getNotify().toJSONString());
        String adminInfo = JSONObject.toJSONString(settingsInfo.getAdmin());
        processTemplates.setSettings(settingsJson);
        processTemplates.setWhoCommit(adminInfo);
        processTemplates.setWhoEdit(adminInfo);
        processTemplates.setWhoExport(adminInfo);
        processTemplates.setRemark(flowEngineDTO.getRemark());
        processTemplates.setIsStop(false);
        Date date= new Date();
        processTemplates.setCreated(date);
        processTemplates.setUpdated(date);
        processTemplates.setUpdated(date);
        processTemplates.setProcessControl(processControl);
        processTemplates.setVersion(version);
        processTemplates.setType(type);
        processTemplateService.save(processTemplates);

        JSONObject jsonObject=new JSONObject();
        jsonObject.put("processJson", processJson);
        jsonObject.put("formJson", formItems);
        BpmnModel bpmnModel = assemBpmnModel(jsonObject, childNode, remark, formName, type,
            templateId);
        Deployment deploy = repositoryService.createDeployment()
                .addBpmnModel(formName + ".bpmn", bpmnModel)
                .name(formName)
                .category(type + "")
                .deploy();
        String processDefinitionId = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult().getId();
        processTemplateService.updateById(ProcessTemplates.builder().templateId(templateId).flowableDefinition(processDefinitionId).build());
        return processDefinitionId;
    }

    private BpmnModel assemBpmnModel(JSONObject jsonObject, ChildNode childNode, String remark,
        String formName, String type, String templateId)
        {
        BpmnModel bpmnModel =new BpmnModel();
        List<SequenceFlow> sequenceFlows = Lists.newArrayList();
        Map<String,ChildNode> childNodeMap=new HashMap<>();
        //  BPMN 模型的目标命名空间
        bpmnModel.setTargetNamespace(type);
        ExtensionAttribute extensionAttribute=new ExtensionAttribute();
        extensionAttribute.setName("DingDing");
        extensionAttribute.setNamespace("http://flowable.org/bpmn");
        extensionAttribute.setValue(jsonObject.toJSONString());
        Process process =new Process();
        // 流程定义的key
        process.setId(WorkFlowConstants.PROCESS_PREFIX+templateId);
        process.setName(formName);
        process.setDocumentation(remark);
        process.addAttribute(extensionAttribute);
        bpmnModel.addProcess(process);

        StartEvent startEvent = createStartEvent();
        process.addFlowElement(startEvent);
            String lastNode = null;
            try {
                lastNode = create(startEvent.getId(), childNode,process,bpmnModel,sequenceFlows,childNodeMap);
            } catch (InvocationTargetException e) {
                e.printStackTrace();
                throw new ServiceException("操作失败");
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                throw new ServiceException("操作失败");
            }
        EndEvent endEvent = createEndEvent();
        process.addFlowElement(endEvent);
        process.addFlowElement(connect(lastNode, endEvent.getId(),sequenceFlows,childNodeMap,process));
        List<FlowableListener> executionListeners =new ArrayList<>();

        //加入业务回调监听器
        FlowableListener myListener=new FlowableListener();
        myListener.setEvent(ExecutionListener.EVENTNAME_END);
        myListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        myListener.setImplementation("${myTaskListener}");
        executionListeners.add(myListener);
        process.setExecutionListeners(executionListeners);
        new BpmnAutoLayout(bpmnModel).execute();
        return bpmnModel;
    }
}
